<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"fengxun2017.github.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.13.2","exturl":false,"sidebar":{"position":"right","display":"hide","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="CPU 缓存是 CPU 使用的一种硬件缓存，该缓存的存储空间小，但访问速度很快。该缓存中存储的是经常访问的主存数据的副本，目的是为了减少 CPU 访问数据的平均时间成本。">
<meta property="og:type" content="article">
<meta property="og:title" content="cpu cache">
<meta property="og:url" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/index.html">
<meta property="og:site_name" content="fx">
<meta property="og:description" content="CPU 缓存是 CPU 使用的一种硬件缓存，该缓存的存储空间小，但访问速度很快。该缓存中存储的是经常访问的主存数据的副本，目的是为了减少 CPU 访问数据的平均时间成本。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/cpu_structure.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/cpu_cache_line.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/cpu_cache_line_data_layout.jpg">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/LRU.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/read.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/write-hit.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/write-no-hit.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/write.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/562px-Write-back_with_write-allocation.svg.png">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/356px-Write-through_with_no-write-allocation.svg.png">
<meta property="article:published_time" content="2022-12-17T16:00:00.000Z">
<meta property="article:modified_time" content="2023-04-05T04:44:02.042Z">
<meta property="article:author" content="feng xun">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/cpu_structure.png">


<link rel="canonical" href="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/","path":"2022/12/18/memory-ordering-cpu-cache/","title":"cpu cache"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>cpu cache | fx</title>
  






  <script async defer data-website-id="" src=""></script>

  <script defer data-domain="" src=""></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
  <a target="_blank" rel="noopener" href="https://github.com/fengxun2017" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">fx</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-CPU-cache%EF%BC%9A"><span class="nav-number">1.</span> <span class="nav-text">1 CPU cache：</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-Cache-line"><span class="nav-number">2.</span> <span class="nav-text">2 Cache line</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#3-Replacement-policies"><span class="nav-number">3.</span> <span class="nav-text">3 Replacement policies</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-Write-policies"><span class="nav-number">4.</span> <span class="nav-text">4 Write policies</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#4-1-%E5%BD%93%E8%A6%81%E5%86%99%E7%9A%84%E6%95%B0%E6%8D%AE%E5%91%BD%E4%B8%AD%E4%BA%86cpu-cache%EF%BC%9A"><span class="nav-number">4.1.</span> <span class="nav-text">4.1 当要写的数据命中了cpu cache：</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#4-2-%E5%BD%93%E8%A6%81%E5%86%99%E7%9A%84%E6%95%B0%E6%8D%AE%E6%B2%A1%E6%9C%89%E5%91%BD%E4%B8%ADcpu-cache%EF%BC%9A"><span class="nav-number">4.2.</span> <span class="nav-text">4.2 当要写的数据没有命中cpu cache：</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99%EF%BC%9A"><span class="nav-number">5.</span> <span class="nav-text">参考资料：</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">feng xun</p>
  <div class="site-description" itemprop="description">FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">96</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">16</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="cc-license site-overview-item animated" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" class="cc-opacity" rel="noopener" target="_blank"><img src="https://cdnjs.cloudflare.com/ajax/libs/creativecommons-vocabulary/2020.11.3/assets/license_badges/small/by_nc_sa.svg" alt="Creative Commons"></a>
  </div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="feng xun">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="fx">
      <meta itemprop="description" content="FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="cpu cache | fx">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          cpu cache
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-12-18 00:00:00" itemprop="dateCreated datePublished" datetime="2022-12-18T00:00:00+08:00">2022-12-18</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/memory-ordering%E7%9B%B8%E5%85%B3/" itemprop="url" rel="index"><span itemprop="name">memory ordering相关</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>5.9k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>7 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <center>
CPU 缓存是 CPU 使用的一种硬件缓存，该缓存的存储空间小，但访问速度很快。该缓存中存储的是经常访问的主存数据的副本，目的是为了减少 CPU 访问数据的平均时间成本。
</center>
<span id="more"></span>

<hr>
<h4 id="1-CPU-cache："><a href="#1-CPU-cache：" class="headerlink" title="1 CPU cache："></a>1 CPU cache：</h4><p>多数 CPU 中的 CPU cache 都是被组织成层次结构，形成多级缓存。<br>如下图所示：一个双核 CPU 中多级缓存的例子。</p>
<p><img src="/2022/12/18/memory-ordering-cpu-cache/cpu_structure.png"></p>
<center>
图片源自【1】
</center>

<p>L1级缓存会再分为L1 指令缓存，和L1 数据缓存。L1拆成指令和数据两个独立的缓存，可以使得流水线在一个周期内同时取缓存中的指令和数据。此外，缓存如果满了，需要有替换策略（删除一个旧的缓存行，给新缓存行腾空间），指令和数据的行为和访问热点区别较大，L1分成独立的指令缓存和数据缓存，可以方便各自使用不同的替换策略。</p>
<p>其中：</p>
<ul>
<li>大小方面：L1缓存 &lt; L2缓存 &lt; L3缓存</li>
<li>速度方面：L1缓存 &gt; L2缓存 &gt; L3缓存</li>
<li>查找顺序，先L1，找不到数据再查L2，之后是L3</li>
<li>关于为什么以多层级的形式组织缓存结构，可以参考这篇文章：<a target="_blank" rel="noopener" href="https://toutiao.io/posts/zafunq/preview">为什么CPU有多层缓存</a></li>
<li>多核CPU中的每个核心，有自己的私有cache（L1、L2），这增强了性能（访问私有cache时，不会和其它核心冲突，所以减少了访问冲突时的处理过程），但也引入了额外的使用复杂性（一个数据同时存在于多个核心的私有cache中，每个核心改变自己的L1 cache中的这个数据时，需要同步给其它核心）。</li>
</ul>
<h4 id="2-Cache-line"><a href="#2-Cache-line" class="headerlink" title="2 Cache line"></a>2 Cache line</h4><p>数据在内存和CPU 缓存之间传输时，是以固定大小的数据块为单位，称为<strong>缓存行（cache line）或缓存块（cache blocks）</strong>。当一个缓存行被从内存中复制到 <code>cpu chache</code> 中时，就会创建一个<strong>缓存条目（cache entry）</strong>。<strong>缓存条目</strong>中包含了从内存中复制的数据（cache line）、该数据在内存中的地址（tag，实际为地址中的一部分）、以及是否有效（flag）标记。<strong>缓存条目</strong>如下图所示：</p>
<table>
<thead>
<tr>
<th align="center">flag bits</th>
<th align="center">tag</th>
<th align="center">cache line</th>
</tr>
</thead>
</table>
<br> 

<p>下图为内存中的一块数据（cache line），被复制到 cpu cache 中的，线程访问该 cache line 中某个位置的数据示例：<br><img src="/2022/12/18/memory-ordering-cpu-cache/cpu_cache_line.png"></p>
<center>
图片源自【1】
</center>
<br> 

<p>CPU需要访问内存中的某个位置时，它首先会在 cpu cache 中检查数据是否在 cache 中，一种查找实现方式（direct mapped cache）如下图所示：<br><img src="/2022/12/18/memory-ordering-cpu-cache/cpu_cache_line_data_layout.jpg"></p>
<center>
图片源自【1】
</center>
<br>
图中所示的 cpu cache，包含了 4K 个**缓存条目（cache entry）**，每个缓存条目中包含的**数据（Data）** 为 16字节（4个 data block，每个data block一般为机器字长，32位机器就是32bit，64为机器为64bit）。

<p>图中，待访问的数据的地址（图中的Address）表示为三部分：</p>
<ul>
<li>Tag：高 16位</li>
<li>Index：4-15位</li>
<li>offset：2-3位</li>
</ul>
<p>访问过程为：根据访问地址（图中的Address）<code>Index</code>，定位到 cpu cache 中的某个 <strong>缓存条目（cache entry）</strong>，比较该<strong>缓存条目</strong>中的<code>Tag</code>是不是和 Address 中的<code>Tag</code>相同。如果相同，并且<strong>缓存条目</strong>的有效标记位（上图中的<code>v</code>）也是有效值，则表明缓存命中（hit），之后根据 Address 中的 <code>offset</code>，从<strong>数据（Data）</strong> 中取出对应位置的数据（图中为一个字，32bit）。<br>反之，如果缓存未命中，则会在 cpu cache 中分配一个新的<strong>缓存条目</strong>，并从内存中复制数据所在的 cache line 到该<strong>缓存条目</strong>中，然后再从cpu cache 中执行访问请求。</p>
<p>多核 CPU中，核心之间的交互式 以及 核心和内存之间的数据同步，都是通过<strong>缓存条目</strong>来进行的，即同步粒度为 cache line，一般x86 CPU的cache line大小为 64字节。</p>
<ul>
<li><code>cache bouncing</code>：多核CPU中，当一个核心，在改写自己的 L1 cache 中的某个 cache line 中的一个数据后，CPU需要执行cache一致性算法，将该 cache line 同步给其它核（如果其它核上的cache 也有这个cache line），这个同步过程相比写 L1 cache 需要更多的处理时间。如果，多核CPU中，两个线程，频繁的修改同一个变量，这会使各自核心上该变量所在的cache line，需要不停的同步给其它核，这会额外引入大量同步时间。可以使用 thread_local来处理这种问题。</li>
<li><code>cache false sharing</code>：如果多核CPU中两个线程，线程A 频繁的修改全局变量A；线程B 频繁的修改全局变量B，变量A 和变量B 在内存中刚好是连续的，在一个cache line中。那么，即使这两个线程访问的是不同的变量，还是会导致两个核心之间需要不停的相互同步cache line。可以使用<code>alignas(64)</code>让变量以cache line的长度对齐，避免它们处于同一个cache line中。</li>
</ul>
<h4 id="3-Replacement-policies"><a href="#3-Replacement-policies" class="headerlink" title="3 Replacement policies"></a>3 Replacement policies</h4><p><code>cpu cache</code>相对主存来说很小，一般从几十KB 到几十 MB。当<code>cpu cache</code>存满后，如果此时访问的一个数据不在<code>cpu cache中</code>，则需要将该数据所在<code>cache line</code>从主存加载到<code>cpu cache</code>中，因此需要从当前<code>cpu cache</code>中删除一条旧的<code>缓存条目</code>（cache entry），来存放新加载的条目。用来选择要删除的缓存条目的方法称为<code>替换策略（Replacement policies）</code>。所有替换策略，都面临一个基本问题：如何预测现有缓存条目中，哪一个在未来最不可能被使用。未来难以预测，所以没有完美的方法在各种可用的替换策略中进行选择。<br>一个比较流行替换策略，是最近最少使用（LRU）策略。该策略总是删除最近最少访问的那个缓存条目。如下图所示，假设 <code>cpu cache</code>中可以存放 4 个缓存条目，数据访问顺序为 A B C D E D F，并且每个数据在主存中属于不同的<code>cache line</code>中，图中数字表示时间信息，数字越小，表示访问的越早：<br><img src="/2022/12/18/memory-ordering-cpu-cache/LRU.png"></p>
<center>
图片源自【5】
</center>
<br>

<p>当缓存未满时，访问的 A B C D 会依次加载到 <code>cpu cache</code>中，并记录下时间信息。<br>当访问 E 时，<code>cpu cache</code>未命中，则需要将其从主存加载到<code>cpu cache</code>中。此时，A 是最近最少访问的，则替换掉 A。<br>当访问 D 时，<code>cpu cache</code>命中，此时，需要更新 D 的访问时间。<br>当访问 F 时，<code>cpu cache</code>未命中，则需要将其从主存加载到<code>cpu cache</code>中。此时，B 是最近最少访问的，则替换掉 B。</p>
<p>如果某些内存区域的数据，几乎不会被再次访问，那么就没必要将其放入<code>cpu cache</code>中。因此，可以将这些内存区域标记为不可缓存，避免了在不会再次访问的情况下，将其数据内容加载到<code>cpu cache</code>中造成的额外开销，提高数据访问性能。</p>
<h4 id="4-Write-policies"><a href="#4-Write-policies" class="headerlink" title="4 Write policies"></a>4 Write policies</h4><p>涉及<code>cpu cache</code>时：<br>对于读理解起来比较简单。<br>要读的数据命中了<code>cpu cache</code>，则直接读出来即可。<br>要读的数据没有命中<code>cpu cache</code>，则将数据从主存中加载到<code>cpu cache</code>中，然后再返回数据。这样，后续的相同的读操作，都可以从<code>cpu cache</code>中立刻得到数据。<br><img src="/2022/12/18/memory-ordering-cpu-cache/read.png"></p>
<p>对于写，就比较麻烦了，当数据命中&#x2F;未命中cpu cache时，均有不同的处理策略。</p>
<h5 id="4-1-当要写的数据命中了cpu-cache："><a href="#4-1-当要写的数据命中了cpu-cache：" class="headerlink" title="4.1 当要写的数据命中了cpu cache："></a>4.1 当要写的数据命中了<code>cpu cache</code>：</h5><p>此时，会更新<code>cpu cache</code>中的相应数据，但数据最终还是得写到主存里的，什么时候将其写回主存中？<strong>用来决定什么时候将 cpu cache 中更新的数据写回主存的策略，就是 write policies</strong>。<br>两个基本的写回主存策略：</p>
<ul>
<li>Write-through：每次写操作更新<code>cpu cache</code>时，同时将数据也写回主从中。耗时久，但是每次更新数据都会立刻同步回主存。</li>
<li>Write-back：每次写时仅更新<code>cpu cache</code>，同时将该<code>缓存条目</code>设置<code>脏标记</code>。之后，当该缓存条目后续被换出时（缓存满了，它又是最久没使用的），如果检测到有<code>脏标记</code>（表明数据有变更），就会将该缓存数据写回到主存中。这种特性也被称为惰性写入（lazy write），数据写回主存的动作被延后了。耗时短，并且可以节省cache和主存间的带宽（例如，当短时间内频繁改动某个cache中的数值，Write-through每次都会将cache中的变动同步回主存，而write-back只更新cache，必要时才会将改动同步回主存）。</li>
</ul>
<p>如下图所示:<br><img src="/2022/12/18/memory-ordering-cpu-cache/write-hit.png">  </p>
<p>PS：对于 Write-through，每次更新数据立刻写回主存会比较慢，一种折中的方案是，更新 cpu cache 后，数据同样“立刻”写回主存，但写操作会暂时保存在一个存储数据队列（store data queue）中，当累积了多个写操作后，再一起更新回主存（提供总线利用率）。</p>
<p>PS：对于Write-back 策略，也不是必须等到缓存条目被换出时，才将数据写回主存（如果数据被更新了），也有其它策略可以主动触发数据（cpu cache 中被更新的数）回写到主存中。例如，当对大量在<code>cpu cache</code>中的数据修改后，可以显示地通知<code>cpu cache</code>，将脏数据写回主存。</p>
<h5 id="4-2-当要写的数据没有命中cpu-cache："><a href="#4-2-当要写的数据没有命中cpu-cache：" class="headerlink" title="4.2 当要写的数据没有命中cpu cache："></a>4.2 当要写的数据没有命中<code>cpu cache</code>：</h5><p>上面的<code>写回策略（write policies）</code>（将<code>cpu cache</code>中的缓存数据写回到主存中），是应用在写操作命中<code>cpu cache</code>后的处理。如果写操作没有命中呢？</p>
<p><strong>没命中 cpu cache 的情况下，使用的处理方式称为 write-miss policies</strong>，同样有两种处理方式：</p>
<ul>
<li>Write allocate（also called fetch on write）：先将数据从主从中加载到<code>cpu cache</code>中，此时数据等于就是命中了，之后就按上面的写操作命中<code>cpu cache</code>的流程走（数据先更新到缓存中，再按<code>Write-through</code>或<code>Write-back</code>策略写回主存中）。</li>
<li>No-write allocate（also called write-no-allocate or write around）：不管<code>cpu cache</code>了，直接将数据更新到主存。</li>
</ul>
<p>如下图所示：<br><img src="/2022/12/18/memory-ordering-cpu-cache/write-no-hit.png"></p>
<p>综上，在写数据情况下。如果数据命中了<code>cpu cache</code>时，先更新<code>cpu cache</code>再将数据写回主存，数据写回主存的时机，有多个策略（<strong>write policies</strong>）可以选择。如果没有命中<code>cpu cache</code>，也存在多种处理方式（<strong>write-miss policies</strong>）。如下图：<br><img src="/2022/12/18/memory-ordering-cpu-cache/write.png"></p>
<p>因此，需要在写回策略（write policies）中选择一个（处理cpu cache命中的情况），以及在 write-miss 处理策略中选择一个（处理cpu cache未命中的情况）。它们是可以任意组合的，但一般情况下它们是这样组合的：</p>
<ul>
<li><p>当使用 write-back 写回策略（立即写cpu cache，但数据延后再写回主存）来处理命中<code>cpu cache</code>的情况时，未命中<code>cpu cache</code>时的处理策略会选择 write allocate（先加载到cpu cache中，后续按命中策略处理）：</p>
<p>  这种组合方式下，写操作后紧接着的读或写都很快，因为数据在cpu cache里。但是，由于使用write-back策略时，cpu cache中的<code>缓存条目</code>有脏标记，所以每次我们需要删除一个旧缓存条目时（腾空间放新数据），需要判断该数据是否已脏，数据脏了需要将其内容写回到主存中（数据写回主存操作被延后到该cache line被换出时）。如下图所示：<br>  <img src="/2022/12/18/memory-ordering-cpu-cache/562px-Write-back_with_write-allocation.svg.png"></p>
  <center>
  图片源自【4】
  </center>
  <br>
  上图中，以写过程为例，解释如下：
  写操作，命中 cpu cache 时：数据写入 cpu cache，标记该数据所在的缓存条目为“脏”（之后，当该缓存条目被换出时，检查到有“脏”标记，就会将数据写回主存中）。
  写操作，未命中 cpu cache时（考虑此时cpu cache已经满了，所以需要删除一个旧的来存新的）：假设，数据从主存加载到cpu cache时，会存储在cpu cache 中的位置 A 处。因此，需要先检查位置 A 处的旧缓存条目是否有“脏”标记。如果没有“脏”标记，新数据直接覆盖该跳缓存条目中的旧数据即可；如果有“脏”标记，则需要先将该缓存条目中的旧数据写回主存，然后再用新数据覆盖该缓存条目中的旧数据。最后，设置这条新的缓存条目中的“脏”标记（因为新数据只写了cpu cache，还没写回主存）。</li>
</ul>
<br>

<ul>
<li><p>当使用 write-through 写回策略（写cpu cache时，同时写回主存）来处理命中<code>cpu cache</code>的情况时，未命中<code>cpu cache</code>时的处理策略会选择 no-write allocate（直接将数据更新到主存中，不管 cpu cache）：</p>
<p>  这是由于，对于命中的情况，既然已经选择了write-through，那么数据每次更新都会同时写入cpu cache和 主存。那么未命中时，就没必要将数据先从主存中加载到cpu cache，再更新。因为这对后续的写没啥积极作用（每次写都会立即写回到主存的）。（后续可能是写操作，也可能是读操作，对于写操作，已经明确了将数据加载到cpu cache中没有积极作用。对于读，这里加载和后续实际读的时候再加载，成本是一样的。所以也就没必要为了不一定会有的读操作，在写操作上引入额外的开销——数据从主存加载到cpu cache的消耗 ）<br>.<br>  这种组合下，对数据的写操作，都是立即更新到主存的，没有延后执行。因此，也就没有“脏”数据的概念了，数据的读写流程更简介：<br>  <img src="/2022/12/18/memory-ordering-cpu-cache/356px-Write-through_with_no-write-allocation.svg.png"></p>
  <center>
  图片源自【4】
  </center>
  <br>
  如上图：

<p>  对于写来说，命中了 cpu cache 就更新 cpu cache，同时更新主存。没命中 cpu cache，直接更新主存即可。<br>  对于读来说，命中了数据，直接返回 cpu cache 中的数据即可。没命中 cpu cache，则将数据从主从中加载到 cpu cache 中，然后再返回数据。</p>
</li>
</ul>
<p>综上：<br>write-back 和 write allocate 的组合方式，平均的读&#x2F;写效率都较高，但当cpu cache 换出的旧缓存条目有“脏”标记时，就需要额外的时间消耗——将该旧缓存条目数据写回主存的时间消耗。</p>
<p>write-through 和 no-write allocate 的组合方式，读操作很快（只有要读的数据不在cache 中时，才涉及从主存将数据加载到cache中。不会因为要换出一个有“脏”标记的旧数据，而引入额外的写主存操作）。但写操较慢，因为每次都需要将数据立即写回到主存中。</p>
<h4 id="参考资料："><a href="#参考资料：" class="headerlink" title="参考资料："></a>参考资料：</h4><p>【1】<a target="_blank" rel="noopener" href="http://gavinchou.github.io/summary/c++/memory-ordering/">http://gavinchou.github.io/summary/c++/memory-ordering/</a><br>【2】<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Memory_ordering">https://en.wikipedia.org/wiki/Memory_ordering</a><br>【3】<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/CPU_cache">https://en.wikipedia.org/wiki/CPU_cache</a><br>【4】<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Cache_(computing)">https://en.wikipedia.org/wiki/Cache_(computing)</a><br>【5】<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU">https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU</a></p>

    </div>

    
    
    

    <footer class="post-footer">
          

<div class="post-copyright">
<ul>
  <li class="post-copyright-author">
      <strong>本文作者： </strong>feng xun
  </li>
  <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/" title="cpu cache">http://fengxun2017.github.io/2022/12/18/memory-ordering-cpu-cache/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2022/12/17/FreeRTOS-use-counting-semaphore/" rel="prev" title="FreeRTOS-使用计数信号量">
                  <i class="fa fa-chevron-left"></i> FreeRTOS-使用计数信号量
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/" rel="next" title="FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV）">
                  FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV） <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2025</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">feng xun</span>
</div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">552k</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">11:30</span>
  </span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
